package org.whl.airapi.util.string;

import org.whl.airapi.util.annotation.RowKeyAnnotation;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;

/**
 * 字符串工具类
 *
 * @author 李昊哲
 * @version 1.0.0
 */
public abstract class StringUtil {
  private static final Charset defaultCharset = StandardCharsets.UTF_8;

  /**
   * 去除字符串左边的的空格
   *
   * @param str 原始字符串
   * @return 去除左边空格后的字符串
   */
  public static String ltrim(String str) {
    return str == null ? null : str.replaceAll("^\\s+", "");
  }

  /**
   * 去除字符串右边的的空格
   *
   * @param str 原始字符串
   * @return 去除右边空格后的字符串
   */
  public static String rtrim(String str) {
    return str == null ? null : str.replaceAll("\\s+$", "");
  }

  /**
   * 判断字符串是否为空
   *
   * @param str 判断参数 string 是否为空
   * @return 首尾尾两端去掉空格后的长度为零 返回 true 其余情况返回 false
   */
  public static boolean isEmpty(String str) {
    return str.trim().isEmpty();
  }

  /**
   * 判断字符串是否为空
   *
   * @param str 判断参数 string 是否为空
   * @return 参数为 null 或者 首尾尾两端去掉空格后的长度为零 返回 true 其余情况返回 false
   */
  public static boolean isBlank(String str) {
    return str == null || str.trim().isEmpty();
  }

  /**
   * 生成UUID带-的字符串，类似于：a5c8a5e8-df2b-4706-bea4-08d0939410e3
   *
   * @return 带-的字符串
   */
  public static String randomUUID() {
    return UUID.randomUUID().toString();
  }

  /**
   * 生成不带-的字符串，类似于：b17f24ff026d40949c85a24f4f375d42
   *
   * @return 不带-的字符串
   */
  public static String simpleUUID() {
    return UUID.randomUUID().toString().replace("-", "");
  }

  /**
   * 随机数字字符串
   *
   * @param count 随机数字字符串长度
   * @return 随机数字字符串
   */
  public static String randomNumeric(int count) {
    String codePool = "0123456789";
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < count; i++) {
      sb.append(codePool.charAt(ThreadLocalRandom.current().nextInt(codePool.length())));
    }
    return sb.toString();
  }

  /**
   * 随机字母字符串
   *
   * @param count 随机字母字符串长度
   * @return 随机字母字符串
   */
  public static String randomAlphabetic(int count) {
    String codePool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < count; i++) {
      sb.append(codePool.charAt(ThreadLocalRandom.current().nextInt(codePool.length())));
    }
    return sb.toString();
  }

  /**
   * 随机字符串
   *
   * @param count 随机字符串长度
   * @return 随机数字字符串
   */
  public static String random(int count) {
    String codePool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < count; i++) {
      sb.append(codePool.charAt(ThreadLocalRandom.current().nextInt(codePool.length())));
    }
    return sb.toString();
  }

  /**
   * 时间补零占位
   *
   * @param time 时间
   * @return 补零后的字符串
   */
  public static String zeroCompensation(int time) {
    return time < 10 ? "0" + time : String.valueOf(time);
  }

  /**
   * 获取文件后缀名
   *
   * @param filename 原始文件名
   * @return 文件后缀名
   */
  public static String getExtension(String filename) {
    int i = filename.lastIndexOf(".");
    return i == -1 ? "" : filename.substring(i + 1);
  }

  /**
   * javabean对象转csv格式字符串
   *
   * @param obj javabean对象
   * @return csv格式字符串
   * @throws IllegalAccessException IllegalAccessException
   */
  public static String format2CSV(Object obj) throws IllegalAccessException {
    if (obj == null) {
      return "";
    }
    // 利用反射获取该对象的 类对象
    Class<?> aClass = obj.getClass();
    // 获取该类对象的是所有属性
    Field[] fields = aClass.getDeclaredFields();
    // 保存最终结果
    StringBuilder sb = new StringBuilder();
    // 遍历属性
    for (Field field : fields) {
      // 设置私有属性可以访问权限
      field.setAccessible(true);
      // 将属性值全部转为字符串
      String str = String.valueOf(field.get(obj));
      sb.append(str).append(",");
    }
    // 返回 最终结果 返回前 去掉字符串中最后一个逗号
    return sb.substring(0, sb.length() - 1);
  }

  /**
   * @param csv    csv文件格式字符串
   * @param rowKey csv文件是否为 RowKeyAnnotation 注解的属性赋值
   * @param clazz  返回值的对象类型
   * @param <T>    返回值的对象类型泛型
   * @return csv文件格式字符串转换后的对象
   * @throws IllegalAccessException    IllegalAccessException
   * @throws NoSuchMethodException     NoSuchMethodException
   * @throws InvocationTargetException InvocationTargetException
   * @throws InstantiationException    InstantiationException
   */
  public static <T> T csv2Bean(String csv, boolean rowKey, Class<T> clazz) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
    csv = csv.trim();
    // rowKey
    String id = null;
    String[] propertyArray = null;
    if (rowKey) {
      id = csv.substring(0, csv.indexOf(","));
      // 将 csv 格式文件转为数组 不包含第一个逗号前的值
      propertyArray = csv.substring(csv.indexOf(",") + 1).split(",");
    } else {
      // 将 csv 格式文件转为数组 包含第一个逗号前的值
      propertyArray = csv.split(",");
    }
    // 获取无参数构造方法
    Constructor<T> constructor = clazz.getConstructor();
    // 使用无参数构造方法创建对象
    T obj = constructor.newInstance();
    // 获取该类对象的是所有属性
    Field[] fields = clazz.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
      Field field = fields[i];
      field.setAccessible(true);
      Class<?> fieldType = field.getType();
      // 获取属性 RowKeyAnnotation 注解
      RowKeyAnnotation annotation = field.getAnnotation(RowKeyAnnotation.class);
      if (rowKey) {
        if (annotation != null) {
          if (fieldType.equals(String.class)) {
            field.set(obj, id);
          } else if (fieldType.equals(Long.class)) {
            field.set(obj, Long.parseLong(id));
          } else if (fieldType.equals(long.class)) {
            field.set(obj, Long.parseLong(id));
          } else if (fieldType.equals(Integer.class)) {
            field.set(obj, Integer.parseInt(id));
          } else if (fieldType.equals(int.class)) {
            field.set(obj, Integer.parseInt(id));
          } else if (fieldType.equals(Double.class)) {
            field.set(obj, Double.parseDouble(id));
          } else if (fieldType.equals(double.class)) {
            field.set(obj, Double.parseDouble(id));
          } else {
            throw new IllegalArgumentException("Unsupported field type: " + fieldType);
          }
        } else {
          if (fieldType.equals(String.class)) {
            field.set(obj, propertyArray[i - 1]); // 直接赋值
          } else if (fieldType.equals(Long.class)) {
            field.set(obj, Long.parseLong(propertyArray[i - 1]));
          } else if (fieldType.equals(long.class)) {
            field.set(obj, Long.parseLong(propertyArray[i - 1]));
          }else if (fieldType.equals(Integer.class)) {
            field.set(obj, Integer.parseInt(propertyArray[i - 1]));
          } else if (fieldType.equals(int.class)) {
            field.set(obj, Integer.parseInt(propertyArray[i - 1]));
          } else if (fieldType.equals(Double.class)) {
            field.set(obj, Double.parseDouble(propertyArray[i - 1]));
          } else if (fieldType.equals(double.class)) {
            field.set(obj, Double.parseDouble(propertyArray[i - 1]));
          } else {
            throw new IllegalArgumentException("Unsupported field type: " + fieldType);
          }
        }
      } else {
        if (annotation != null) {
          continue;
        }
        if (fieldType.equals(String.class)) {
          field.set(obj, propertyArray[i - 1]); // 直接赋值
        } else if (fieldType.equals(Long.class)) {
          field.set(obj, Long.parseLong(propertyArray[i - 1]));
        } else if (fieldType.equals(long.class)) {
          field.set(obj, Long.parseLong(propertyArray[i - 1]));
        }else if (fieldType.equals(Integer.class)) {
          field.set(obj, Integer.parseInt(propertyArray[i - 1]));
        } else if (fieldType.equals(int.class)) {
          field.set(obj, Integer.parseInt(propertyArray[i - 1]));
        } else if (fieldType.equals(Double.class)) {
          field.set(obj, Double.parseDouble(propertyArray[i - 1]));
        } else if (fieldType.equals(double.class)) {
          field.set(obj, Double.parseDouble(propertyArray[i - 1]));
        } else {
          throw new IllegalArgumentException("Unsupported field type: " + fieldType);
        }
      }
    }
    return obj;
  }

  /**
   * 接收 JSON 字符串，去掉开头和结尾的方括号，按对象划分。
   * 通过 extractKeys 方法提取字段名，添加到 CSV 的第一行。
   * 逐项解析 JSON 对象，并通过 extractValues 获取对应的值，添加到 CSV 的后续行。
   *
   * @param jsonString json格式字符串
   * @param header     输出是否包含头标题 true包含 false不包含
   * @return csv 格式字符串
   */
  public static String convertJsonToCsv(String jsonString, boolean header) {
    StringBuilder csvBuilder = new StringBuilder();

    // 假设 JSON 是一个数组的形式
    if (jsonString.startsWith("[") && jsonString.endsWith("]")) {
      // 去掉开头和结尾的方括号
      jsonString = jsonString.substring(1, jsonString.length() - 1).trim();
      String[] jsonObjects = jsonString.split("},\\s*\\{"); // 按对象划分

      // 处理第一项获取字段名
      if (header) {
        String[] keys = extractKeys(jsonObjects[0]);
        csvBuilder.append(String.join(",", keys)).append("\n");
      }
      // 逐个处理 JSON 对象
      for (String jsonObject : jsonObjects) {
        jsonObject = jsonObject.replaceAll("[{}\"]", ""); // 去掉大括号和双引号
        String[] values = extractValues(jsonObject);
        csvBuilder.append(String.join(",", values)).append("\n");
      }
    } else {
      // 处理第一项获取字段名
      if (header) {
        String[] keys = extractKeys(jsonString);
        csvBuilder.append(String.join(",", keys)).append("\n");
      }
      String[] values = extractValues(jsonString);
      csvBuilder.append(String.join(",", values)).append("\n");
    }
    return csvBuilder.substring(0, csvBuilder.length() - 1);
  }

  /**
   * 提取 JSON 对象的键，通过分隔符将 JSON 对象的属性解析成键的字符串数组。
   *
   * @param jsonString json格式字符串
   * @return JSON 对象的键
   */
  public static String[] extractKeys(String jsonString) {
    String[] parts = jsonString.split(",");
    String[] keys = new String[parts.length];

    for (int i = 0; i < parts.length; i++) {
      keys[i] = parts[i].split(":")[0].trim().replace("{", "").replace("}", "").replace("\"", "");
    }
    return keys;
  }

  /**
   * 提取 JSON 对象的键，通过分隔符将 JSON 对象的属性解析成键的字符串数组。
   *
   * @param jsonString json格式字符串
   * @return JSON 对象的键字符串
   */
  public static String extractKeysToString(String jsonString) {
    String[] values = extractKeys(jsonString);
    return String.join(",", values);
  }

  /**
   * 提取 JSON 对象的值，根据键的顺序，从 JSON 对象中对应地提取值。
   *
   * @param jsonString json格式字符串
   * @return JSON 对象的值
   */
  public static String[] extractValues(String jsonString) {
    String[] parts = jsonString.split(",");
    String[] values = new String[parts.length];

    for (int i = 0; i < parts.length; i++) {
      String value = parts[i].split(":")[1].trim().replace("{", "").replace("}", "").replace("\"", "");
      values[i] = value; // 此处直接取值，假设没有嵌套和特殊字符
    }
    return values;
  }

  /**
   * 提取 JSON 对象的值，根据键的顺序，从 JSON 对象中对应地提取值。
   *
   * @param jsonString json格式字符串
   * @return JSON 对象的值字符串
   */
  public static String extractValuesToString(String jsonString) {
    String[] values = extractValues(jsonString);
    return String.join(",", values);
  }

  /**
   * MD5加密
   *
   * @param origin 原始字符串
   * @return MD5加密后新的字符串
   */
  public static String md5Hex(String origin) {
    try {
      // 获取MD5算法的MessageDigest实例
      MessageDigest md5 = MessageDigest.getInstance("MD5");
      // 计算哈希值
      byte[] hash = md5.digest(origin.getBytes());
      // 将哈希值转换为十六进制字符串
      StringBuilder builder = new StringBuilder();
      for (byte b : hash) {
        builder.append(String.format("%02x", b));
      }
      return builder.toString();
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * URLEncoder
   *
   * @param origin 原始字符串
   * @return URL encode后的字符串
   */
  public static String urlEncode(String origin) throws UnsupportedEncodingException {
    return URLEncoder.encode(origin, defaultCharset.name());
  }

  /**
   * URLEncoder
   *
   * @param string  原始字符串
   * @param charset 字符集
   * @return URL encode后的字符串
   */
  public static String urlEncode(String string, Charset charset) throws UnsupportedEncodingException {
    return URLEncoder.encode(string, defaultCharset.name());
  }

  /**
   * URLEncoder
   *
   * @param string URL encode后的字符串
   * @return URL decode后的字符串
   */
  public static String urlDecoder(String string) throws UnsupportedEncodingException {
    return URLDecoder.decode(string, defaultCharset.name());
  }

  /**
   * URLEncoder
   *
   * @param string  URL encode后的字符串
   * @param charset 字符集
   * @return URL decode后的字符串
   */
  public static String urlDecoder(String string, Charset charset) throws UnsupportedEncodingException {
    return URLDecoder.decode(string, defaultCharset);
  }

  /**
   * Base64Encode
   *
   * @param string 原始字符串
   * @return Base64 decode后的字符串
   */
  public static String base64Encode(String string) {
    return base64Encode(string, defaultCharset);
  }

  /**
   * Base64Encode
   *
   * @param string  原始字符串
   * @param charset 字符集
   * @return Base64 decode后的字符串
   */
  public static String base64Encode(String string, Charset charset) {
    Base64.Encoder encoder = Base64.getEncoder();
    return encoder.encodeToString(string.getBytes(charset));
  }

  /**
   * Base64Decoder
   *
   * @param string Base64 decode后的字符串
   * @return 原始字符串
   */
  public static String base64Decoder(String string) {
    return base64Decoder(string, defaultCharset);
  }

  /**
   * Base64Decoder
   *
   * @param string  Base64 decode后的字符串
   * @param charset 字符集
   * @return 原始字符串
   */
  public static String base64Decoder(String string, Charset charset) {
    Base64.Decoder decoder = Base64.getDecoder();
    return new String(decoder.decode(string), charset);
  }

  /**
   * 根据身份证获取性别 1代表男性 0代表女性
   *
   * @param idCard 身份证
   * @return 性别 1代表男性 0代表女性
   */
  public static int getGenderByIdCard(String idCard) {
    return Integer.parseInt(idCard.substring(16, 17)) % 2;
  }

  /**
   * 数据库里下划线命名规则转化为java里面驼峰式命名
   *
   * @param filedName 字段名称
   * @return javabean属性名称
   */
  public static String nameInDb2nameInJava(String filedName) {
    String coluname = filedName.toLowerCase();
    //正则
    if (Pattern.compile("^\\S+_+\\S+$").matcher(coluname).find()) {
      char[] ca = coluname.toCharArray();
      for (int j = 1; j < ca.length - 1; j++) {
        if (ca[j] == '_') {
          ca[j] = '\0';
          ca[j + 1] = Character.toUpperCase(ca[j + 1]);
        }
      }
      coluname = new String(ca);
    }
    return coluname.replaceAll("\0", "");
  }

  /**
   * javabean对象转成 key制表符value逗号value 的格式
   *
   * @param obj javabean对象
   * @return csv格式字符串
   * @throws IllegalAccessException IllegalAccessException
   */
  public static String format2Hdfs(Object obj) throws IllegalAccessException {
    if (obj == null) {
      return "";
    }
    // 利用反射获取该对象的 类对象
    Class<?> aClass = obj.getClass();
    // 获取该类对象的是所有属性
    Field[] fields = aClass.getDeclaredFields();
    // 保存最终结果
    StringBuilder sb = new StringBuilder();
    // 遍历属性
    for (Field field : fields) {
      // 设置私有属性可以访问权限
      field.setAccessible(true);
      // 获取属性 RowKeyAnnotation 注解
      RowKeyAnnotation annotation = field.getAnnotation(RowKeyAnnotation.class);
      // 将属性值全部转为字符串
      String str = String.valueOf(field.get(obj));
      if (annotation != null) {
        sb.append(str).append("\t");
      } else {
        sb.append(str).append(",");
      }
    }
    // 返回 最终结果 返回前 去掉字符串中最后一个逗号
    return sb.substring(0, sb.length() - 1);
  }
}
